#include <cv.h>
#include <highgui.h>
#include <iostream>
#include <cxcore.h>
#include <math.h>

struct Item
{
    int m_id;
    bool m_haveGroup;
    cv::RotatedRect m_box;
};

void WriteCountorIndexOnImage(cv::Mat &src, const cv::RotatedRect &box, const int &idx)
{
    //Escreve o índice do contorno
    std::ostringstream oss;
    oss << idx;
    std::string text(oss.str());
    const int pointOffset = 10;
    cv::Point textPoint = cv::Point(box.center.x + box.size.width/2 - pointOffset, box.center.y - box.size.height/2 - pointOffset );

    cv::putText( src, 
                 text, 
                 textPoint,
                 CV_FONT_HERSHEY_SIMPLEX, 
                 0.6,
                 cv::Scalar(0, 255, 255));

    cv::line(src, textPoint, box.center, cv::Scalar(120, 120, 120), 1, CV_AA);
}

void FindPlateRects( cv::vector<Item> &src)
{
    cv::vector<cv::vector<Item> > group;
    cv::vector<Item> sub_group;
        

    cv::vector<Item>::iterator it1 = src.begin();
    cv::vector<Item>::iterator it2;
    
    for (; it1 != src.end(); ++it1)
    {
        //se o item já tem um grupo
        if ((*it1).m_haveGroup) continue; 

        //cria um novo grupo
        sub_group.push_back(*it1);
        (*it1).m_haveGroup = true;

        for (it2 = it1 + 1; it2 != src.end(); ++it2)
        {
            if ((*it2).m_haveGroup) continue;

            //Verifica o offset em Y
            if ( abs( (*it1).m_box.center.y - (*it2).m_box.center.y ) < 8.0)
            {
                sub_group.push_back(*it2);
                (*it2).m_haveGroup = true;
            }
            
        }

        //Verifica o número de elementos do subgrupo
        if (sub_group.size() > 4)
        {
            group.push_back(sub_group);
            std::cout << "Achei um grupo com " << sub_group.size() << " elementos." << std::endl;

            //imprimindo elementos
            for (it2 = sub_group.begin(); it2 != sub_group.end(); ++it2)
            {
                std::cout << (*it2).m_id << std::endl;
            }
        }

        //Avalia os offsets em X e elimina os exageros

        sub_group.clear();
    }
}

int main( int argc, char** argv )
{
	cv::Mat grayImg, thrImg;

    char* filename = argc == 2 ? argv[1] : (char*)"../imgs/8.jpg";
    if( (grayImg = cv::imread(filename,0)).data == 0 )
        return -1;

    //Filtro
	cv::medianBlur(grayImg,grayImg,5);

    //imagem escala de cinza
    cv::namedWindow("Gray Image",0);
	cv::imshow("Gray Image",grayImg);	

	//Threshold adaptativo
	cv::adaptiveThreshold(grayImg,thrImg,255.0,CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY,31,5.0);
	cv::bitwise_not(thrImg,thrImg);
	cv::namedWindow("Threshold Image",0);
	cv::imshow("Threshold Image",thrImg);

    //Variáveis para contorno
    double area;
    cv::vector<cv::vector<cv::Point> > contours;
    cv::vector<cv::Vec4i> hierarchy;
    cv::Mat contoursImg = cv::Mat::zeros(thrImg.rows, thrImg.cols, CV_8UC3);
    cv::namedWindow( "Components", 0 );

    //Vetor que armazena contornos válidos
    //cv::vector<cv::vector<cv::Point> > valid_contours;
    cv::vector<Item> valid_box_group;

    //Encontra contornos na imagem e desenha
    //CV_RETR_CCOMP      
    findContours( thrImg, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

    int idx = 0;
	int count = 0;
    bool goAhead = false;
    for( ; idx >= 0; idx = hierarchy[idx][0] )
    {
        char key;
        Item item;
        //Calcula a área
        area = cv::contourArea(contours[idx]);
        if (area < 200.0 || area > 2500.0) continue;

        //Encontra retangulos
        cv::Point2f vtx[4];
        cv::RotatedRect box = cv::minAreaRect(contours[idx]);

		//Avalia proporção dos retângulos
		double prop = cv::max(box.size.width, box.size.height)/ cv::min(box.size.width, box.size.height);
		
        //prop < 1.4|| prop > 1.9
		if ( prop < 1.4|| prop > 2.3) continue; 

		//Desenha retangulos
        box.points(vtx);
        for(int i = 0; i < 4; i++ )
            cv::line(contoursImg, vtx[i], vtx[(i+1)%4], cv::Scalar(0, 255, 0), 1, CV_AA);
       
        //Escreve os números
        WriteCountorIndexOnImage(contoursImg, box, count);
		
		//Desenha contornos
        cv::Scalar color(255, 255, 255);
        cv::drawContours( contoursImg, contours, idx, color, CV_FILLED, 8, hierarchy );
		cv::imshow( "Components", contoursImg );

        //contornos válidos
        //valid_contours.push_back(contours.at(idx));
        item.m_haveGroup = false;
        item.m_box = box;
        item.m_id = count;
        valid_box_group.push_back(item);

		//Saida padrão
		std::cout << count << "\t" << idx << "\tprop: " << prop << "\tarea: " << area << "\tcentro: (" << box.center.x << ", " << box.center.y << ")" << std::endl;
		count++;

        if (!goAhead) 
        	key = cv::waitKey();

        if ((int)key == 27) break;

        if ((int)key == 103) goAhead = true;
    }

    FindPlateRects( valid_box_group );

    if (goAhead)
    	cv::waitKey();
    return 0;
}
